<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>深拷贝和浅拷贝</title>
</head>

<body>
	<script>
		/*
			+深拷贝和浅拷贝
				==>存储的是该对象在栈中引用，真实的数据存放在堆内存里
				==>浅拷贝（shallowCopy）只是增加了一个指针指向已存在的内存地址（地址不变）
				==>深拷贝（deepCopy）是增加了一个指针并且申请了一个新的内存，使这个增加的指针指向这个新的内存（开辟一个新的地址）

				实现深复制的方法
				JSON.parse(JSON.stringify())
		*/
		//浅复制
		function shallowCopy(obj) {
			if (typeof obj !== 'object') return

			let newObj = obj instanceof Array ? [] : {}
			for (let key in obj) {
				//对象.hasOwnProperty(key):判断对象是否包含特定的自身（非继承）属性
				if (obj.hasOwnProperty(key)) {
					newObj[key] = obj[key]
				}
			}
			return newObj
		}

		//简单版的深复制
		function deepClone(obj) {
			if (typeof obj !== 'object') return;
			var newObj = obj instanceof Array ? [] : {};
			for (var key in obj) {
				if (obj.hasOwnProperty(key)) {
					newObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key];
				}
			}
			return newObj;
		}

		var obj1 = { a: 1, arr: [2, 3] };
		let obj2 = shallowCopy(obj1)
		console.log(obj1)
		console.log(obj2)
		//改变obj2中的arr，obj1也会改变
		obj2.arr[1] = 5;
		console.log(obj1)
		console.log(obj2)

		//复杂版深克隆：基于简单版的基础上，还考虑了内置对象比如 Date、RegExp 等对象和函数以及解决了循环引用的问题。
		const isObject = (target) => (typeof target === "object" || typeof target === "function") && target !== null;

		function deepClone(target, map = new WeakMap()) {
			if (map.get(target)) {
				return target;
			}
			// 获取当前值的构造函数：获取它的类型
			let constructor = target.constructor;
			// 检测当前对象target是否与正则、日期格式对象匹配
			if (/^(RegExp|Date)$/i.test(constructor.name)) {
				// 创建一个新的特殊对象(正则类/日期类)的实例
				return new constructor(target);
			}
			if (isObject(target)) {
				map.set(target, true);  // 为循环引用的对象做标记
				const cloneTarget = Array.isArray(target) ? [] : {};
				for (let prop in target) {
					if (target.hasOwnProperty(prop)) {
						cloneTarget[prop] = deepClone(target[prop], map);
					}
				}
				return cloneTarget;
			} else {
				return target;
			}
		}
	</script>
</body>

</html>